module i2c_bit_shift(
	clk			,	//系统时钟	
	rst_n			,	//系统复位
	cmd			,	//控制总线实现各种传输操作的各种命令的组合
	tx_data		,	//总线要发送的8位数据，需要传输的数据经此端口传入该模块
	go				,	//整个模块的启动使能信号
	ack_o			,	//从机是否应答标志
	rx_data		,	//总线收到的8位数据，读操作时读到的数据由此端口输出
	i2c_sclk		,	//i2c时钟总线
	i2c_sdat		,	//i2c数据总线
	trans_done	 	//发送或接收8位数据完成标志信号
);
	input 					clk			;			
	input 					rst_n			;				
	input 			[5:0]	cmd			;				
	input 			[7:0]	tx_data		;			
	input 					go				;	
	output	reg 	[7:0] rx_data		;
	output	reg			ack_o			;	
	output	reg			i2c_sclk		;		
	inout						i2c_sdat		;			
	output	reg			trans_done	;	
	
	localparam	IDLE			=	7'b0000001,	//空闲状态		
					GEN_STA		=	7'b0000010, //产生起始信号
					WR_DATA		=	7'b0000100, //写数据状态
					RD_DATA		=	7'b0001000, //读数据状态
					CHECK_ACK	=	7'b0010000,	//检测应答状态
					GEN_ACK		=	7'b0100000, //产生应答状态
					GEN_STO		=	7'b1000000; //产生停止信号
						
	localparam	WR				=	6'b000001 ,	//写请求		
					STA			=	6'b000010 , //起始位请求
					RD				=	6'b000100 , //读请求
					STO			=	6'b001000 , //停止位请求
					ACK			=	6'b010000 ,	//应答为请求
					NACK			=	6'b100000 ; //无应答位请求
					
	parameter	SYS_CLOCK = 50_000_000;
	parameter	SCL_CLOCK = 400_000;
	localparam 	SCL_CNT_M = SYS_CLOCK/SCL_CLOCK/4-1;
	
	reg [19:0] 	div_cnt;
	reg 			en_div_cnt;
	reg			i2c_sdat_o;
	reg			i2c_sdat_oe;
	reg [4:0]	cnt;
	reg [6:0]   state;
	
	wire			sclk_plus;
	
	assign sclk_plus = (div_cnt == SCL_CNT_M)?1'b1:1'b0;
	assign i2c_sdat = !i2c_sdat_o && i2c_sdat_oe ?1'b0:1'bz;
	
	always @ (posedge clk or negedge rst_n)
	if (!rst_n)
		div_cnt <= 20'd0;
	else if (en_div_cnt)
		begin
			if (div_cnt == SCL_CNT_M)
				div_cnt <= 20'd0;
			else
				div_cnt <= div_cnt + 1'b1;
		end
	else
		div_cnt <= 20'd0;
		
	always @ (posedge clk or negedge rst_n)
	if (!rst_n)
		begin
			state <= IDLE;
			rx_data <= 8'd0;
			ack_o <= 1'b0;
			en_div_cnt <= 1'b0;
			i2c_sdat_o <= 1'b1;
			i2c_sdat_oe <= 1'b0;
			cnt <= 5'd0;
			trans_done <= 1'b0;
		end
	else
		begin
			case (state)
				IDLE		:	begin
									trans_done <= 1'b0;
									i2c_sdat_oe <= 1'b1;
									if (go)
										begin
											en_div_cnt <= 1'b1;
											if (cmd & STA)
												state <= GEN_STA;
											else if (cmd & WR)
												state <= WR_DATA;
											else if (cmd & RD)
												state <= RD_DATA;
											else
												state <= IDLE;
										end
									else
										begin
											state <= IDLE;
											en_div_cnt <= 1'b0;
										end
								end
						
				GEN_STA	:	begin
									if (sclk_plus)
										begin
											if (cnt == 5'd3)
												cnt <= 5'd0;
											else 
												cnt <= cnt + 1'b1;
											case(cnt)
												0	:	begin i2c_sdat_o <= 1'b1; i2c_sdat_oe <= 1'b1; end	
												1	:	begin i2c_sclk <= 1'b1; end
												2	:	begin i2c_sdat_o <= 1'b0; i2c_sclk <= 1'b1;end
												3	:	begin i2c_sclk <= 1'b0;end
												default : begin i2c_sdat_o <= 1'b1; i2c_sclk <= 1'b1; end	
											endcase
											if (cnt == 3)
												begin
													if (cmd & WR)
														state <= WR_DATA;
													else if (cmd & RD)
														state <= RD_DATA;
												end
										end
								end
								
				WR_DATA	:	begin
									if (sclk_plus)
										begin
											if (cnt == 5'd31)
												cnt <= 5'd0;
											else
												cnt <= cnt + 1'b1;
											case (cnt)
												0,4,8,12,16,20,24,28:begin i2c_sdat_o <= tx_data[7-cnt[4:2]]; i2c_sdat_oe <= 1'd1;end	//set data;
												1,5,9,13,17,21,25,29:begin i2c_sclk <= 1'b1;end		//sclk posedge
												2,6,10,14,18,22,26,30:begin i2c_sclk <= 1'b1;end	//sclk keep high
												3,7,11,15,19,23,27,31:begin i2c_sclk <= 1'b0;end	//sclk negedge
												default : begin i2c_sdat_o <= 1'b1; i2c_sclk <= 1'b1;end
											endcase
											if (cnt == 5'd31)
												begin
													state <= CHECK_ACK;
												end
										end
								end
								
								
				RD_DATA	:	begin
									if (sclk_plus)
										begin
											if (cnt == 5'd31)
												cnt <= 5'd0;
											else
												cnt <= cnt + 1'b1;
											case (cnt)
												0,4,8,12,16,20,24,28:begin i2c_sclk <= 1'b0; i2c_sdat_oe <= 1'b0;end	//set data;
												1,5,9,13,17,21,25,29:begin i2c_sclk <= 1'b1;end		//sclk posedge
												2,6,10,14,18,22,26,30:begin i2c_sclk <= 1'b1;rx_data <= {rx_data[6:0],i2c_sdat};end	//sclk keep high
												3,7,11,15,19,23,27,31:begin i2c_sclk <= 1'b0;end	//sclk negedge
												default : begin i2c_sdat_o <= 1'b1; i2c_sclk <= 1'b1;end
											endcase
											if (cnt == 5'd31)
												begin
													state <= GEN_ACK;
												end
										end
								end

				CHECK_ACK :	begin
									if (sclk_plus)
										begin
											if (cnt == 5'd3)
												cnt <= 5'd0;
											else
												cnt <= cnt + 1'b1;
											case(cnt)
												0	:	begin i2c_sdat_oe <= 1'b0; i2c_sclk <= 1'b0; end
												1	:	begin i2c_sclk <= 1'b1;  end
												2	:	begin i2c_sclk <= 1'b1; ack_o <= i2c_sdat; end
												3	:	begin i2c_sclk <= 1'b0; end
												default : begin i2c_sdat_o <= 1'b1; i2c_sclk <= 1'b1; end
											endcase
											if (cnt == 3)
												begin
													if (cmd & STO)
														state <= GEN_STO;
													else 
														begin
															state <= IDLE;
															trans_done <= 1'b1;
														end
												end
										end
								end
								
				GEN_ACK	:	begin
									if (sclk_plus)
										begin
											if (cnt == 5'd3)
												cnt <= 5'd0;
											else
												cnt <= cnt + 1'b1;
											case(cnt)
												0	:	begin i2c_sdat_oe <= 1'b1; i2c_sclk <= 1'b0; 
															if (cmd & ACK)
																i2c_sdat_o <= 1'b0;
															else if (cmd & NACK)
																i2c_sdat_o <= 1'b1;	
														end
												1	:	begin i2c_sclk <= 1'b1; end
												2	:	begin i2c_sclk <= 1'b1; end
												3	:	begin i2c_sclk <= 1'b0; end
												default : begin i2c_sdat_o <= 1'b1; i2c_sclk <= 1'b1; end
											endcase
											if (cnt == 5'd3)
												begin
													if (cmd & STO)
														state <= GEN_STO;
													else 
														begin
															state <= IDLE;
															trans_done <= 1'b1;
														end
												end
										end
								end

				GEN_STO	:	begin
									if (sclk_plus)
										begin
											if (cnt == 5'd3)
												cnt <= 5'd0;
											else
												cnt <= cnt + 1'b1;
											case(cnt)
												0	:	begin i2c_sdat_oe <= 1'b1; i2c_sdat_o <= 1'b0; end
												1	:	begin i2c_sclk <= 1'b1;  end
												2	:	begin i2c_sdat_o <= 1'b1; i2c_sclk <= 1'b1; end
												3	:	begin i2c_sclk <= 1'b1; end
												default : begin i2c_sdat_o <= 1'b1; i2c_sclk <= 1'b1; end
											endcase
											if (cnt == 5'd3)
												begin
													trans_done <= 1'b1;
													state <= IDLE;
												end
										end
								end
				default : state <= IDLE;
			endcase
		end
	
endmodule 